﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace FrogEngine
{
	public class Triangle : IPrimitive
	{
		public Triangle(Vector3 v1, Vector3 v2, Vector3 v3, int i1, int i2, int i3)
		{
			this.v1 = v1;
			this.v2 = v2;
			this.v3 = v3;
			this.i1 = i1;
			this.i2 = i2;
			this.i3 = i3;
		}

		public IEnumerable<Vector3> Points
		{
			get { return new Vector3[] { v1, v2, v3 }; }
		}

		private Vector3 v1;
		public Vector3 V1
		{
			get { return v1; }
			set { v1 = value; }
		}

		private Vector3 v2;
		public Vector3 V2
		{
			get { return v2; }
			set { v2 = value; }
		}

		private Vector3 v3;
		public Vector3 V3
		{
			get { return v3; }
			set { v3 = value; }
		}

		private int i1;
		public int I1
		{
			get { return i1; }
			set { i1 = value; }
		}

		private int i2;
		public int I2
		{
			get { return i2; }
			set { i2 = value; }
		}

		private int i3;
		public int I3
		{
			get { return i3; }
			set { i3 = value; }
		}

		public double Area
		{
			//Area = sqrt(s*(s-a)*(s-b)*(s-c)) 
			get 
			{
				float a = V1p1.Length;
				float b = V1p2.Length;
				float c = V2p2.Length;
				var s = (a + b + c) / 2;
				return Math.Sqrt(s * (s - a) * (s - b) * (s - c)); 
			}
		}

		#region Segments

		public LineSegment V1p1
		{
			get { return new LineSegment(v1, v2); }
		}

		public LineSegment V1p2
		{
			get { return new LineSegment(v1, v3); }
		}

		public LineSegment V2p1
		{
			get { return new LineSegment(v2, v1); }
		}

		public LineSegment V2p2
		{
			get { return new LineSegment(v2, v3); }
		}

		public LineSegment V3p1
		{
			get { return new LineSegment(v3, v1); }
		}

		public LineSegment V3p2
		{
			get { return new LineSegment(v3, v2); }
		}

		#endregion

		public Vector3 Normal
		{
			get { return Vector3.Cross(V2 - V1, V3 - V1); }
		}

		public Vector3 FlippedNormal
		{
			get { return Vector3.Cross(V3 - V1, V2 - V1); }
		}

		public Vector3 Centroid
		{
			get { return (V1 + V2 + V3) / 3f; }
		}

		public bool Intersects(Triangle t)
		{
			return CollisionHelper.TriangleTriangleIntersection(this, t);
		}

		public bool Intersects(Ray r)
		{
			Vector3 v1 = this.V1;
			Vector3 v2 = this.V2;
			Vector3 v3 = this.V3;
			float? result;
			CollisionHelper.RayIntersectsTriangle(ref r, ref v1, ref v2, ref v3, out result);
			return result == null ? false : true;
		}

		public bool Intersects(Ray r, out float dis)
		{
			Vector3 v1 = this.V1;
			Vector3 v2 = this.V2;
			Vector3 v3 = this.V3;
			float? result;
			CollisionHelper.RayIntersectsTriangle(ref r, ref v1, ref v2, ref v3, out result);
			dis = result.HasValue ? result.Value : 0f;
			return result == null ? false : true;
		}

		public float[] this[int index]
		{
			get
			{
				if (index == 0)
					return new float[] { this.V1.X, this.V2.X, this.V3.X };
				else if (index == 1)
					return new float[] { this.V1.Y, this.V2.Y, this.V3.Y };
				else if (index == 2)
					return new float[] { this.V1.Z, this.V2.Z, this.V3.Z };
				else
					return null;
			}
		}

		public static bool operator ==(Triangle t1, Triangle t2)
		{
			return t1.V1 == t2.V1 && t1.V2 == t2.V2 && t1.V3 == t2.V3;
		}

		public bool CollisionEquals(Triangle t2)
		{
			bool v1 = PrecisionHelper.Equal(this.V1.X, t2.V1.X)
				&& PrecisionHelper.Equal(this.V1.Y, t2.V1.Y)
				&& PrecisionHelper.Equal(this.V1.Z, t2.V1.Z);

			bool v2 = PrecisionHelper.Equal(this.V2.X, t2.V2.X)
				&& PrecisionHelper.Equal(this.V2.Y, t2.V2.Y)
				&& PrecisionHelper.Equal(this.V2.Z, t2.V2.Z);

			bool v3 = PrecisionHelper.Equal(this.V3.X, t2.V3.X)
				&& PrecisionHelper.Equal(this.V3.Y, t2.V3.Y)
				&& PrecisionHelper.Equal(this.V3.Z, t2.V3.Z);

			return v1 && v2 && v3;
		}

		public static bool operator !=(Triangle t1, Triangle t2)
		{
			return !(t1 == t2);
		}

		public override bool Equals(object obj)
		{
			return this == (Triangle)obj;
		}

		public override int GetHashCode()
		{
			return base.GetHashCode();
            //int v1 = BitConverter.ToInt32(BitConverter.GetBytes(V1.X), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V1.Y), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V1.Z), 0);
            //int v2 = BitConverter.ToInt32(BitConverter.GetBytes(V2.X), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V2.Y), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V2.Z), 0);
            //int v3 = BitConverter.ToInt32(BitConverter.GetBytes(V3.X), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V3.Y), 0) ^ BitConverter.ToInt32(BitConverter.GetBytes(V3.Z), 0);
            //return v1 ^ v2 ^ v3;
			
			//string hash = V1.ToString() + ":" + V2.ToString() + ":" + V3.ToString();
			//return hash.GetHashCode();
		}
	}
}
